﻿Imports System
Imports System.Collections.Generic
Imports System.ComponentModel
Imports System.ComponentModel.Design
Imports System.Drawing
Imports System.Workflow.ComponentModel
Imports System.Workflow.ComponentModel.Compiler

Namespace System.Workflow.Activities
    <ActivityValidator(GetType(ListenValidator)), ToolboxBitmap(GetType(ListenActivity), "Resources.Listen.png"), SRCategory("Standard"), SRDescription("ListenActivityDescription"), ToolboxItem(GetType(ListenToolboxItem)), Designer(GetType(ListenDesigner), GetType(IDesigner))> _
    Public NotInheritable Class ListenActivity
        Inherits CompositeActivity
        Implements IActivityEventListener([Of] ActivityExecutionStatusChangedEventArgs)
        ' Methods
        Public Sub New()
        End Sub

        Public Sub New(ByVal name As String)
            MyBase.New(name)
        End Sub

        Protected Overrides Function Cancel(ByVal executionContext As ActivityExecutionContext) As ActivityExecutionStatus
            If (executionContext Is Nothing) Then
                Throw New ArgumentNullException("executionContext")
            End If
            If (Not Me.ActivityState Is Nothing) Then
                If Me.IsListenTrigerred Then
                    Dim i As Integer
                    For i = 0 To MyBase.EnabledActivities.Count - 1
                        Dim activity As EventDrivenActivity = [TryCast](MyBase.EnabledActivities.Item(i),EventDrivenActivity)
                        If (activity.ExecutionStatus = ActivityExecutionStatus.Executing) Then
                            executionContext.CancelActivity(activity)
                            Return ActivityExecutionStatus.Canceling
                        End If
                        If (activity.ExecutionStatus = ActivityExecutionStatus.Faulting) Then
                            Return ActivityExecutionStatus.Canceling
                        End If
                    Next i
                Else
                    Dim j As Integer
                    For j = 0 To MyBase.EnabledActivities.Count - 1
                        Dim activity2 As EventDrivenActivity = [TryCast](MyBase.EnabledActivities.Item(j),EventDrivenActivity)
                        Dim parentEventHandler As ListenEventActivitySubscriber = Me.ActivityState.Item(j)
                        activity2.EventActivity.Unsubscribe(executionContext, parentEventHandler)
                    Next j
                End If
                Me.ActivityState = Nothing
            End If
            Return ActivityExecutionStatus.Closed
        End Function

        Protected Overrides Function Execute(ByVal executionContext As ActivityExecutionContext) As ActivityExecutionStatus
            If (executionContext Is Nothing) Then
                Throw New ArgumentNullException("executionContext")
            End If
            Dim list As New List([Of] ListenEventActivitySubscriber)
            Me.ActivityState = list
            Dim i As Integer
            For i = 0 To MyBase.EnabledActivities.Count - 1
                Dim eventDriven As EventDrivenActivity = [TryCast](MyBase.EnabledActivities.Item(i),EventDrivenActivity)
                Dim item As New ListenEventActivitySubscriber(eventDriven)
                list.Add(item)
                eventDriven.EventActivity.Subscribe(executionContext, item)
            Next i
            Return ActivityExecutionStatus.Executing
        End Function

        Protected NotOverridable Overrides Sub OnActivityChangeAdd(ByVal executionContext As ActivityExecutionContext, ByVal addedActivity As Activity)
            If (executionContext Is Nothing) Then
                Throw New ArgumentNullException("executionContext")
            End If
            If (addedActivity Is Nothing) Then
                Throw New ArgumentNullException("addedActivity")
            End If
            Dim activity As ListenActivity = [TryCast](executionContext.Activity,ListenActivity)
            If (((activity.ExecutionStatus = ActivityExecutionStatus.Executing) AndAlso (Not activity.ActivityState Is Nothing)) AndAlso Not activity.IsListenTrigerred) Then
                Dim eventDriven As EventDrivenActivity = [TryCast](addedActivity,EventDrivenActivity)
                Dim parentEventHandler As New ListenEventActivitySubscriber(eventDriven)
                eventDriven.EventActivity.Subscribe(executionContext, parentEventHandler)
                activity.ActivityState.Insert(activity.EnabledActivities.IndexOf(addedActivity), parentEventHandler)
            End If
        End Sub

        Protected NotOverridable Overrides Sub OnActivityChangeRemove(ByVal executionContext As ActivityExecutionContext, ByVal removedActivity As Activity)
            If (executionContext Is Nothing) Then
                Throw New ArgumentNullException("executionContext")
            End If
            If (removedActivity Is Nothing) Then
                Throw New ArgumentNullException("removedActivity")
            End If
            Dim activity As ListenActivity = [TryCast](executionContext.Activity,ListenActivity)
            If (((activity.ExecutionStatus = ActivityExecutionStatus.Executing) AndAlso (Not activity.ActivityState Is Nothing)) AndAlso Not activity.IsListenTrigerred) Then
                Dim activity2 As EventDrivenActivity = [TryCast](removedActivity,EventDrivenActivity)
                Dim i As Integer
                For i = 0 To activity.ActivityState.Count - 1
                    Dim parentEventHandler As ListenEventActivitySubscriber = activity.ActivityState.Item(i)
                    If parentEventHandler.eventDrivenActivity.QualifiedName.Equals(activity2.QualifiedName) Then
                        activity2.EventActivity.Unsubscribe(executionContext, parentEventHandler)
                        activity.ActivityState.RemoveAt(i)
                        Return
                    End If
                Next i
            ElseIf (Me.IsListenTrigerred AndAlso (removedActivity.ExecutionStatus = ActivityExecutionStatus.Closed)) Then
                Me.activeBranchRemoved = True
            End If
        End Sub

        Protected Overrides Sub OnClosed(ByVal provider As IServiceProvider)
            MyBase.RemoveProperty(ListenActivity.IsListenTrigerredProperty)
            MyBase.RemoveProperty(ListenActivity.ActivityStateProperty)
        End Sub

        Protected Overrides Sub OnWorkflowChangesCompleted(ByVal executionContext As ActivityExecutionContext)
            MyBase.OnWorkflowChangesCompleted(executionContext)
            If Me.activeBranchRemoved Then
                executionContext.CloseActivity
            End If
            Me.activeBranchRemoved = False
        End Sub

        Private Sub System.Workflow.ComponentModel.IActivityEventListener<System.Workflow.ComponentModel.ActivityExecutionStatusChangedEventArgs>.OnEvent(ByVal sender As Object, ByVal e As ActivityExecutionStatusChangedEventArgs) Implements IActivityEventListener([Of] ActivityExecutionStatusChangedEventArgs).OnEvent
            If (sender Is Nothing) Then
                Throw New ArgumentNullException("sender")
            End If
            If (e Is Nothing) Then
                Throw New ArgumentNullException("e")
            End If
            Dim context As ActivityExecutionContext = [TryCast](sender,ActivityExecutionContext)
            If (context Is Nothing) Then
                Throw New ArgumentException(SR.Error_SenderMustBeActivityExecutionContext, "sender")
            End If
            e.Activity.UnregisterForStatusChange(Activity.ClosedEvent, Me)
            context.CloseActivity
        End Sub


        ' Properties
        Private Property ActivityState As List([Of] ListenEventActivitySubscriber)
            Get
                Return DirectCast(MyBase.GetValue(ListenActivity.ActivityStateProperty), List([Of] ListenEventActivitySubscriber))
            End Get
            Set(ByVal value As List([Of] ListenEventActivitySubscriber))
                If (value Is Nothing) Then
                    MyBase.RemoveProperty(ListenActivity.ActivityStateProperty)
                Else
                    MyBase.SetValue(ListenActivity.ActivityStateProperty, value)
                End If
            End Set
        End Property

        Private Property IsListenTrigerred As Boolean
            Get
                Return CBool(MyBase.GetValue(ListenActivity.IsListenTrigerredProperty))
            End Get
            Set(ByVal value As Boolean)
                MyBase.SetValue(ListenActivity.IsListenTrigerredProperty, value)
            End Set
        End Property


        ' Fields
        <NonSerialized> _
        Private activeBranchRemoved As Boolean
        Private Shared ActivityStateProperty As DependencyProperty = DependencyProperty.Register("ActivityState", GetType(List([Of] ListenEventActivitySubscriber)), GetType(ListenActivity))
        Private Shared IsListenTrigerredProperty As DependencyProperty = DependencyProperty.Register("IsListenTrigerred", GetType(Boolean), GetType(ListenActivity), New PropertyMetadata(False))

        ' Nested Types
        <Serializable> _
        Private NotInheritable Class ListenEventActivitySubscriber
            Implements IActivityEventListener([Of] QueueEventArgs)
            ' Methods
            Friend Sub New(ByVal eventDriven As EventDrivenActivity)
                Me.eventDrivenActivity = eventDriven
            End Sub

            Private Sub System.Workflow.ComponentModel.IActivityEventListener<System.Workflow.ComponentModel.QueueEventArgs>.OnEvent(ByVal sender As Object, ByVal e As QueueEventArgs) Implements IActivityEventListener([Of] QueueEventArgs).OnEvent
                If (sender Is Nothing) Then
                    Throw New ArgumentNullException("sender")
                End If
                If (e Is Nothing) Then
                    Throw New ArgumentNullException("e")
                End If
                Dim parentContext As ActivityExecutionContext = [TryCast](sender,ActivityExecutionContext)
                If (parentContext Is Nothing) Then
                    Throw New ArgumentException(SR.Error_SenderMustBeActivityExecutionContext, "sender")
                End If
                Dim activityStatusChangeListener As ListenActivity = [TryCast](parentContext.Activity,ListenActivity)
                If (((Not activityStatusChangeListener.IsListenTrigerred AndAlso (activityStatusChangeListener.ExecutionStatus <> ActivityExecutionStatus.Canceling)) AndAlso (activityStatusChangeListener.ExecutionStatus <> ActivityExecutionStatus.Closed)) AndAlso activityStatusChangeListener.EnabledActivities.Contains(Me.eventDrivenActivity)) Then
                    activityStatusChangeListener.IsListenTrigerred = True
                    Dim i As Integer
                    For i = 0 To activityStatusChangeListener.EnabledActivities.Count - 1
                        Dim activity2 As EventDrivenActivity = [TryCast](activityStatusChangeListener.EnabledActivities.Item(i),EventDrivenActivity)
                        Dim parentEventHandler As ListenEventActivitySubscriber = activityStatusChangeListener.ActivityState.Item(i)
                        activity2.EventActivity.Unsubscribe(parentContext, parentEventHandler)
                    Next i
                    Me.eventDrivenActivity.RegisterForStatusChange(Activity.ClosedEvent, activityStatusChangeListener)
                    parentContext.ExecuteActivity(Me.eventDrivenActivity)
                End If
            End Sub


            ' Fields
            Friend eventDrivenActivity As EventDrivenActivity
        End Class
    End Class
End Namespace

